package graphics.engine;

import java.awt.Font;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.media.opengl.*;

import com.sun.opengl.util.j2d.TextRenderer;
import settings.Settings;
import ui.timeline.TimeLine;
import view.MainView;

import colors.Color;

import model.MainModel;

import controler.KeyboardControler;
import controler.MouseControler;
import controler.SelectionManager;

public class RenderEngine implements GLEventListener 
{
	private Camera camera;
	private ProgramShader programShader; 
	
	private MouseControler mouseControler;
	private KeyboardControler keyboardControler;
	
	private SelectionManager selectionManager;
	
	private MainView view;
	private MainModel model;

	private TimeLine timeLine;
	private RenderEngineType type;
	private Color clearColor = new Color(160,160,160);
	private long offset = 0;
	private long logTime = 0;
	
	private TextureManager textureManager;
	
	private long lastDisplayTime = -1;
	private long logTimeWithOffset;
	
	private TextRenderer textRenderer;
	
	public boolean draw = false;

	public RenderEngine(MainModel model, TimeLine timeLine, Date startTime) 
	{
		this.setModel(model);
		this.timeLine = timeLine;
		this.type = RenderEngineType.FloorPlan;
		this.setSelectionManager(new SelectionManager());

		this.textureManager = new TextureManager();
	    this.programShader = new ProgramShader();
		this.setCamera(new Camera());
		this.setView(new MainView(this.getModel()));
		
	    this.setMouseControler(new MouseControler(this));
	    this.setKeyboardControler(new KeyboardControler(this));
	}
	

	@Override
	public void display(GLAutoDrawable drawable) 
	{
		GL gl = drawable.getGL();
		gl.glClear(GL.GL_COLOR_BUFFER_BIT);
		if(!Settings.isExperimentMode || this.draw)
		{
			long elapsedTime;
			long currentTime = System.currentTimeMillis();
			if(lastDisplayTime == -1)
			{
				logTime = this.timeLine.getBeginTime();
				lastDisplayTime = currentTime;
			}
			
			elapsedTime = (long) ((currentTime - lastDisplayTime) * Settings.speedFactor);
			
			if(Settings.isPlaybackEnable)
			{
				logTime = logTime + elapsedTime;
			}
			
			
			logTimeWithOffset = logTime - offset;
			
			if(!this.timeLine.sliderIsDragging() && !this.timeLine.rangeSliderIsDragging())
			{	 

				this.timeLine.setCurrentValue(logTimeWithOffset);
				this.timeLine.repaint();
			}
			else if(this.timeLine.sliderIsDragging())
			{
				
				offset = logTime - this.timeLine.getCurrentValue();
				
				SimpleDateFormat ft = new SimpleDateFormat ("HH:mm:ss");
			}
			else if( this.timeLine.rangeSliderIsDragging() )
			{
				this.view.trajectoryesHaveChanged(true);
			}
			
			
			long beginTime = this.timeLine.getRangeLowerBound();
			long endTime = this.timeLine.getRangeUpperBound();
			
			gl.glUseProgram(this.programShader.ProgramShaderID);
	
		    this.getView().resetStat();
			
			float[] mat = getCamera().ProjectionMatrix(gl);
			gl.glUniformMatrix4fv(this.programShader.ProjectionMatrixUniformID, 1, false, mat, 0);	
	
			mat = getCamera().ViewMatrix();
			gl.glUniformMatrix4fv(this.programShader.ViewMatrixUniformID, 1, false, mat, 0);	
	
			this.getView().Draw(drawable, this.programShader);
			
			if(Settings.DrawTrajetories)
			{
				this.getView().DrawTrajectories(drawable, this.programShader, logTimeWithOffset, beginTime, endTime);
				this.getView().DrawTargets(drawable, this.programShader, logTimeWithOffset);
			}
			
			this.getMouseControler().getLasso().Draw(drawable, this.programShader);
			lastDisplayTime = currentTime;
			
			gl.glUseProgram(0);
			mat = getCamera().ProjectionMatrix(gl);
			gl.glMatrixMode(GL.GL_PROJECTION);
			gl.glLoadMatrixf(mat, 0);
	
			mat = getCamera().ViewMatrix();
			gl.glMatrixMode(GL.GL_MODELVIEW);
			gl.glLoadMatrixf(mat , 0);
			
	
			this.getView().DrawRoomLabel(drawable);
		}
	}

	@Override
	public void init(GLAutoDrawable drawable) 
	{
		GL gl = drawable.getGL();		
		
		this.SetState(gl);
		this.programShader.init(gl);
		
		this.textRenderer = new TextRenderer(new Font("SansSerif",Font.CENTER_BASELINE, 50), true, true);
		
		try {
			this.getView().Init(gl, this.textRenderer, this.textureManager);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		if(this.type == RenderEngineType.Topological)
		{
			this.view.collapseAllRooms();
		}
		else if(this.type == RenderEngineType.FloorPlan)
		{
			this.view.expendAllRooms();
		}
		
		if( !Settings.isExperimentMode )
		{
			this.getCamera().Reset();
			this.getCamera().CenterTo(this.getView().getBoundingBox());
		}
	}
	
	public void SetState(GL gl)
	{
		gl.setSwapInterval(1);
		
		gl.glClearColor(clearColor.R, clearColor.G, clearColor.B, 1.0f);
		
		
		gl.glEnable (GL.GL_BLEND);
		gl.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_ONE_MINUS_SRC_ALPHA);
	    gl.glShadeModel(GL.GL_SMOOTH);
	    
	    gl.glLineWidth(2.0f);
	    
	    gl.glEnable(GL.GL_LINE_SMOOTH); 
	    gl.glEnable(GL.GL_POINT_SMOOTH );
	    gl.glEnable(GL.GL_POLYGON_SMOOTH);
	    gl.glEnable(GL.GL_PERSPECTIVE_CORRECTION_HINT);
	    
	    gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
	    gl.glHint(GL.GL_POINT_SMOOTH_HINT, GL.GL_NICEST);
	    gl.glHint(GL.GL_LINE_SMOOTH_HINT, GL.GL_NICEST);
	    gl.glHint(GL.GL_POLYGON_SMOOTH_HINT, GL.GL_NICEST);
	    
	   //gl.glPolygonMode(GL2.GL_FRONT_AND_BACK, GL2.GL_LINE);
	}

	@Override
	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) 
	{
		GL gl = drawable.getGL();
		gl.glViewport(0, 0, width, height);
		
		this.getCamera().Resize(width, height);
		this.getMouseControler().Resize(width, height);
		
		getCamera().ProjectionHasChanged = true;
	}

	public Camera getCamera() {
		return camera;
	}

	public void setCamera(Camera camera) {
		this.camera = camera;
	}

	public MainView getView() {
		return view;
	}

	public void setView(MainView view) {
		this.view = view;
	}


	public KeyboardControler getKeyboardControler() {
		return keyboardControler;
	}


	public void setKeyboardControler(KeyboardControler keyboardControler) {
		this.keyboardControler = keyboardControler;
	}


	public MouseControler getMouseControler() {
		return mouseControler;
	}


	public void setMouseControler(MouseControler mouseControler) {
		this.mouseControler = mouseControler;
	}


	@Override
	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {
		// TODO Auto-generated method stub
		
	}


	public SelectionManager getSelectionManager() {
		return selectionManager;
	}


	public void setSelectionManager(SelectionManager selectionManager) {
		this.selectionManager = selectionManager;
	}


	public RenderEngineType getType() {
		return type;
	}


	public void setType(RenderEngineType type) 
	{
		this.type = type;
		if(this.type == RenderEngineType.Topological)
		{
			this.view.collapseAllRooms();
		}
		else if(this.type == RenderEngineType.FloorPlan)
		{
			this.view.expendAllRooms();
		}
	}


	public void selectAllRooms() {
		this.selectionManager.addAllRoom(this.view.rooms);
	}


	public void reset() {
		// TODO Auto-generated method stub
		this.draw = true;

		this.getCamera().Reset();
		this.getCamera().CenterTo(this.getView().getBoundingBox());
	}


	public MainModel getModel() {
		return model;
	}


	public void setModel(MainModel model) {
		this.model = model;
	}
}
